﻿// This file is part of Code4Public (http://code4public.codeplex.com)
// Copyright 2011 Sina Iravanian - <sina@sinairv.com>
//
// This source file(s) may be redistributed, altered and customized
// by any means PROVIDING the authors name and all copyright
// notices remain intact.
// THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED. USE IT AT YOUR OWN RISK. THE AUTHOR ACCEPTS NO
// LIABILITY FOR ANY DATA DAMAGE/LOSS THAT THIS PRODUCT MAY CAUSE.
// ------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.Diagnostics;

namespace Code4Public.GUI
{
    public partial class BorderEditor : UserControl
    {
        private int m_nInternalPadding;
        private int m_nBorderThickness;
        private bool m_bTopBorder;
        private bool m_bBottomBorder;
        private bool m_bRightBorder;
        private bool m_bLeftBorder;

        private Color m_colorHoverColor;
        private Color m_colorSelectedColor;
        private Color m_colorDeselectedColor;
        private Color m_colorLineColor;

        public BorderEditor()
        {
            InitializeComponent();

            this.m_colorHoverColor = Color.Olive;
            this.m_colorSelectedColor = Color.Black;
            this.m_colorDeselectedColor = Color.White;
            this.m_colorLineColor = Color.Gainsboro;
            this.m_nInternalPadding = 5;
            this.m_nBorderThickness = 5;
            this.m_bTopBorder = false;
            this.m_bBottomBorder = false;
            this.m_bRightBorder = false;
            this.m_bLeftBorder = false;
        }

        public Color HoverColor
        {
            get
            {
                return m_colorHoverColor;
            }

            set
            {
                this.m_colorHoverColor = value;
                this.Invalidate();
            }
        }

        public Color SelectedColor
        {
            get
            {
                return m_colorSelectedColor;
            }

            set
            {
                this.m_colorSelectedColor = value;
                this.Invalidate();
            }
        }

        public Color DeselectedColor
        {
            get
            {
                return m_colorDeselectedColor;
            }

            set
            {
                this.m_colorDeselectedColor = value;
                this.Invalidate();
            }
        }

        public Color LineColor
        {
            get
            {
                return m_colorLineColor;
            }

            set
            {
                this.m_colorLineColor = value;
                this.Invalidate();
            }
        }

        public int InternalPadding
        {
            get
            {
                return this.m_nInternalPadding;
            }

            set
            {
                m_nInternalPadding = value;
                this.Invalidate();
            }
        }

        public int BorderThickness
        {
            get
            {
                return this.m_nBorderThickness;
            }

            set
            {
                this.m_nBorderThickness = value; 
                this.Invalidate();
            }
        }

        public bool TopBorder
        {
            get
            {
                return this.m_bTopBorder;
            }

            set
            {
                this.m_bTopBorder = value;
                this.Invalidate();
                if (BorderSelectionChanged != null)
                    BorderSelectionChanged(this, new EventArgs());
            }
        }

        public bool BottomBorder
        {
            get
            {
                return m_bBottomBorder;
            }

            set
            {
                this.m_bBottomBorder = value;
                this.Invalidate();
                if (BorderSelectionChanged != null)
                    BorderSelectionChanged(this, new EventArgs());
            }
        }

        public bool RightBorder
        {
            get
            {
                return this.m_bRightBorder;
            }

            set
            {
                this.m_bRightBorder = value;
                this.Invalidate();
                if (BorderSelectionChanged != null)
                    BorderSelectionChanged(this, new EventArgs());
            }
        }

        public bool LeftBorder
        {
            get
            {
                return m_bLeftBorder;
            }

            set
            {
                this.m_bLeftBorder = value;
                this.Invalidate();
                if (BorderSelectionChanged != null)
                    BorderSelectionChanged(this, new EventArgs());
            }
        }

        public event EventHandler BorderSelectionChanged;

        private void BorderEditor_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;

            g.FillRectangle(new SolidBrush(this.BackColor), 0, 0, this.Width, this.Height);
            DrawDefaultBorders(g);
        }

        private void DrawDefaultBorders(Graphics g)
        {
            DrawTopBorder(g, TopBorder ? BorderDrawState.Selected : BorderDrawState.Deselected);
            DrawBottomBorder(g, BottomBorder ? BorderDrawState.Selected : BorderDrawState.Deselected);
            DrawLeftBorder(g, LeftBorder ? BorderDrawState.Selected : BorderDrawState.Deselected);
            DrawRightBorder(g, RightBorder ? BorderDrawState.Selected : BorderDrawState.Deselected);
        }

        private void DrawTopBorder(Graphics g, BorderDrawState state)
        {
            Color color = GetColorForDrawState(state);

            Point[] points = new Point[] {
                new Point(this.InternalPadding, this.InternalPadding),
                new Point(this.Width - this.InternalPadding, this.InternalPadding),
                new Point(this.Width - this.InternalPadding-this.BorderThickness,  this.InternalPadding + this.BorderThickness),
                new Point(this.InternalPadding + this.BorderThickness, this.InternalPadding + this.BorderThickness),
                new Point(this.InternalPadding, this.InternalPadding)
            };

            g.FillPolygon(new SolidBrush(color), points);
            g.DrawLines(new Pen(LineColor, 1.0f), points);
        }

        private void DrawBottomBorder(Graphics g, BorderDrawState state)
        {
            Color color = GetColorForDrawState(state);

            Point[] points = new Point[] {
                new Point(this.InternalPadding, this.Height - this.InternalPadding),
                new Point(this.Width - this.InternalPadding, this.Height - this.InternalPadding),
                new Point(this.Width - this.InternalPadding - this.BorderThickness,  this.Height - this.InternalPadding - this.BorderThickness),
                new Point(this.InternalPadding + this.BorderThickness, this.Height - this.InternalPadding - this.BorderThickness),
                new Point(this.InternalPadding, this.Height - this.InternalPadding)
            };

            g.FillPolygon(new SolidBrush(color), points);
            g.DrawLines(new Pen(LineColor, 1.0f), points);
        }

        private void DrawRightBorder(Graphics g, BorderDrawState state)
        {
            Color color = GetColorForDrawState(state);

            Point[] points = new Point[] {
                new Point(this.Width - this.InternalPadding, this.InternalPadding),
                new Point(this.Width - this.InternalPadding, this.Height - this.InternalPadding),
                new Point(this.Width - this.InternalPadding - this.BorderThickness, this.Height - this.InternalPadding - this.BorderThickness),
                new Point(this.Width - this.InternalPadding - this.BorderThickness, this.InternalPadding + this.BorderThickness),
                new Point(this.Width - this.InternalPadding, this.InternalPadding)
            };

            g.FillPolygon(new SolidBrush(color), points);
            g.DrawLines(new Pen(LineColor, 1.0f), points);
        }

        private void DrawLeftBorder(Graphics g, BorderDrawState state)
        {
            Color color = GetColorForDrawState(state);

            Point[] points = new Point[] {
                new Point(this.InternalPadding, this.InternalPadding),
                new Point(this.InternalPadding + this.BorderThickness, this.InternalPadding + this.BorderThickness),
                new Point(this.InternalPadding + this.BorderThickness, this.Height - this.InternalPadding - this.BorderThickness),
                new Point(this.InternalPadding, this.Height - this.InternalPadding),
                new Point(this.InternalPadding, this.InternalPadding)
            };

            g.FillPolygon(new SolidBrush(color), points);
            g.DrawLines(new Pen(LineColor, 1.0f), points);
        }

        private Color GetColorForDrawState(BorderDrawState state)
        {
            Color color = Color.Black;
            switch (state)
            {
                case BorderDrawState.Deselected:
                    color = DeselectedColor;
                    break;
                case BorderDrawState.Selected:
                    color = SelectedColor;
                    break;
                case BorderDrawState.Hovered:
                    color = HoverColor;
                    break;
                default:
                    break;
            }
            return color;
        }

        private void BorderEditor_Resize(object sender, EventArgs e)
        {
            this.Invalidate();
        }

        private bool IsInTopBorder(int x, int y)
        {
            return ((y < x) && (x < Width - y) && (this.InternalPadding < y) && (y < this.InternalPadding + this.BorderThickness));
        }

        private bool IsInBottomBorder(int x, int y)
        {
            return ((Height - y < x) && (x < y + Width - Height) && (Height - InternalPadding - BorderThickness < y) && (y < Height - InternalPadding));
        }

        private bool IsInLeftBorder(int x, int y)
        {
            return ((InternalPadding < x) && (x < InternalPadding + BorderThickness) && (x < y) && (y < -x + Height));
        }

        private bool IsInRightBorder(int x, int y)
        {
            return ((Width - InternalPadding - BorderThickness < x) && (x < Width - InternalPadding) && 
                (-x + Width < y) && (y < x - Width + Height));
        }

        #region State Variables to enhance hovering effect performance
        private bool m_isTopBorderHovered = false;
        private bool m_isBottomBorderHovered = false;
        private bool m_isRightBorderHovered = false;
        private bool m_isLeftBorderHovered = false;

        private bool IsAnyBorderHovered
        {
            get
            {
                return m_isTopBorderHovered || m_isRightBorderHovered ||
                    m_isLeftBorderHovered || m_isBottomBorderHovered;
            }

            set
            {
                if (value) return;
                m_isBottomBorderHovered = false;
                m_isLeftBorderHovered = false;
                m_isRightBorderHovered = false;
                m_isTopBorderHovered = false;
            }
        }

        #endregion

        private void BorderEditor_MouseMove(object sender, MouseEventArgs e)
        {
            int x = e.X;
            int y = e.Y;

            // Debug.WriteLine(e.X.ToString() + " : " + e.Y.ToString());
            if (IsInTopBorder(x, y))
            {
                if (!m_isTopBorderHovered)
                {
                    IsAnyBorderHovered = false;
                    m_isTopBorderHovered = true;

                    DrawDefaultBorders(Graphics.FromHwnd(this.Handle));
                    DrawTopBorder(Graphics.FromHwnd(this.Handle), BorderDrawState.Hovered);
                }
            }
            else if (IsInRightBorder(x, y))
            {
                if (!m_isRightBorderHovered)
                {
                    IsAnyBorderHovered = false;
                    m_isRightBorderHovered = true;

                    DrawDefaultBorders(Graphics.FromHwnd(this.Handle));
                    DrawRightBorder(Graphics.FromHwnd(this.Handle), BorderDrawState.Hovered);
                }
            }
            else if (IsInBottomBorder(x, y))
            {
                if (!m_isBottomBorderHovered)
                {
                    IsAnyBorderHovered = false;
                    m_isBottomBorderHovered = true;

                    DrawDefaultBorders(Graphics.FromHwnd(this.Handle));
                    DrawBottomBorder(Graphics.FromHwnd(this.Handle), BorderDrawState.Hovered);
                }
            }
            else if (IsInLeftBorder(x, y))
            {
                if (!m_isLeftBorderHovered)
                {
                    IsAnyBorderHovered = false;
                    m_isLeftBorderHovered = true;

                    DrawDefaultBorders(Graphics.FromHwnd(this.Handle));
                    DrawLeftBorder(Graphics.FromHwnd(this.Handle), BorderDrawState.Hovered);
                }
            }
            else
            {
                if (IsAnyBorderHovered)
                {
                    DrawDefaultBorders(Graphics.FromHwnd(this.Handle));
                    IsAnyBorderHovered = false;
                }
            }
        }

        private void BorderEditor_MouseLeave(object sender, EventArgs e)
        {
            if (IsAnyBorderHovered)
            {
                DrawDefaultBorders(Graphics.FromHwnd(this.Handle));
                IsAnyBorderHovered = false;
            }
        }

        private void BorderEditor_MouseUp(object sender, MouseEventArgs e)
        {
            int x = e.X;
            int y = e.Y;
            // Debug.WriteLine(e.X.ToString() + " : " + e.Y.ToString());
            if (IsInTopBorder(x, y))
            {
                TopBorder = !TopBorder;
            }
            else if (IsInRightBorder(x, y))
            {
                RightBorder = !RightBorder;
            }
            else if (IsInBottomBorder(x, y))
            {
                BottomBorder = !BottomBorder;
            }
            else if (IsInLeftBorder(x, y))
            {
                LeftBorder = !LeftBorder;
            }
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            if (LeftBorder)
                sb.Append("Left ");
            if (RightBorder)
                sb.Append("Right ");
            if (TopBorder)
                sb.Append("Top ");
            if (BottomBorder)
                sb.Append("Bottom ");

            return sb.ToString();
        }

        private enum BorderDrawState
        {
            Deselected,
            Selected,
            Hovered
        }

    }
}
